﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
using C4F.VistaP2P.Common;
using System.ComponentModel.Design;
using System.Collections;
using C4F.VistaP2P.Win.Chat;
using C4F.VistaP2P.Win.File;
using C4F.VistaP2P.Win.Media;
using C4F.VistaP2P.Win.Network;
using C4F.VistaP2P.Win.Picture;
using C4F.VistaP2P.Win;

namespace C4F.VistaP2P.Win.Network
{
    /// <summary>
    /// This NetworkManager or the component NetworkManagerADV is required to use
    /// any of the Peer 2 Peer Controls that are part of Coding 4 Fun.
    /// </summary>
    [Description("This control or the component < NetworkManager(ADV) > is necessary to use any of the C4F P2P Controls." +
                 "The only difference between the two is that this one is a control and the other is a component"),
     ToolboxBitmap(typeof(resfinder), "C4F_P2PWinformControls.c4f.bmp"),
    Designer(typeof(NetworkDesigner))]    
    public partial class NetworkManager : UserControl
    {

        
        /// <summary>
        /// The PeerChannel class is put in a different thread than the UI so the UI remains responsive. 
        /// </summary>
        private BackgroundWorker mPeerChannelThread;

        /// <summary>
        /// the lower level Peer2PeerLibrary
        /// </summary>
        private P2PLib mP2PLib;


        /// <summary>
        /// Used for streaming audio through a local url
        /// </summary>
        private StreamingHttpListener mAudioHttpListener;
        /// <summary>
        /// Used for streaming video through a local url
        /// </summary>
        private StreamingHttpListener mVideoHttpListener;


        /// <summary>
        /// This constructor just initializes the form and hooks up the default join and leave buttons.
        /// </summary>
        public NetworkManager()
        {
            InitializeComponent();
            
            ////set up the events now with the appropriate buttons...
            btnJoin.Click += new EventHandler(JoinNetwork_Click);
            btnLeave.Click += new EventHandler(LeaveNetwork_Click);
            
        }

        #region Attached Controls

        /// <summary>
        /// A reference to the audio control if one is assigned.
        /// </summary>
        private MediaControl mAudioControl = null;

        /// <summary>
        /// Gets or Sets the Audio control to use with this NetworkManager.
        /// </summary>
        [
           Description("Set the Audio Control to use with this NetworkManager"),
           Category("P2P"),
        ]
        public MediaControl Audio
        {
            get { return mAudioControl; }
            set
            {
                if (mAudioControl != null)
                {
                    mAudioControl.SendingStream -= new EventHandler<SendStreamEventArgs>(Audio_SendingStream);
                    mAudioControl.CancelStream -= new EventHandler(Audio_CancelStream);
                }
                
                mAudioControl = value;
                if (mAudioControl != null)
                {
                    mAudioControl.lblThirdLineLabel.Visible = true;
                    mAudioControl.ThirdLineValue.Visible = true;                                    
                    mAudioControl.SendingStream += new EventHandler<SendStreamEventArgs>(Audio_SendingStream);
                    mAudioControl.CancelStream += new EventHandler(Audio_CancelStream);
                }               
            }
        }

        
        /// <summary>
        /// A reference to the Video Control if one is assigned.
        /// </summary>
        private MediaControl mVideoControl = null;

        /// <summary>
        /// Gets or Sets the Video control to use with this NetworkManager.
        /// </summary>
        [
           Description("Set the Video Control to use with this NetworkManager"),
           Category("P2P"),
        ]
        public MediaControl Video
        {
            get { return mVideoControl; }
            set
            {
                if (mVideoControl != null)
                {
                    mVideoControl.SendingStream -= new EventHandler<SendStreamEventArgs>(Video_SendingStream);
                    mVideoControl.CancelStream -= new EventHandler(Video_CancelStream);
                }

                mVideoControl = value;
                if (mVideoControl != null)
                {
                    mVideoControl.lblThirdLineLabel.Visible = false;
                    mVideoControl.ThirdLineValue.Visible = false;                
                    //mMediaControl.SetConnectionStatus(mState);
                    mVideoControl.SendingStream += new EventHandler<SendStreamEventArgs>(Video_SendingStream);
                    mVideoControl.CancelStream += new EventHandler(Video_CancelStream);
                }
            }
        }


        
        /// <summary>
        /// A reference to the Picture Control if one is assigned. 
        /// </summary>
        private PictureControl mPicture = null;

        
        /// <summary>
        /// The Picture control to use with this NetworkManager.
        /// </summary>
        [
           Description("Set the Picture Control to use with this NetworkManager"),
           Category("P2P"),
        ]
        public PictureControl Picture
        {
            get { return mPicture; }
            set
            {
                if (mPicture != null)
                {
                    mPicture.SendingPicture -= new EventHandler<PictureChangedEventArgs>(Picture_SendingPicture);
                }

                mPicture = value;
                if (mPicture != null)
                {
                    mPicture.SetConnectionStatus(mState);
                    mPicture.SendingPicture += new EventHandler<PictureChangedEventArgs>(Picture_SendingPicture);
                }
            }
        }


        
        /// <summary>
        /// A reference to the Chat Control if one is assigned. 
        /// </summary>
        private ChatControl mChat = null;

        /// <summary>
        /// The Chat control to use with this NetworkManager.
        /// </summary>
        [

           Description("Set the Chat Control to use with this NetworkManager"),
           Category("P2P"),
        ]
        public ChatControl Chat
        {
            get { return mChat; }
            set
            {
                if (mChat != null)
                {
                    mChat.SendingText -= new EventHandler<ChatChangedEventArgs>(Chat_SendingText);
                }

                mChat = value;
                if (mChat != null)
                {
                    mChat.SetConnectionStatus(mState);
                    mChat.SendingText += new EventHandler<ChatChangedEventArgs>(Chat_SendingText);
                }
            }
        }

        //A reference to the File Control if one is assigned.
        private FileControl mFile = null;

        /// <summary>
        /// The File control to use with this NetworkManager
        /// </summary>        
        [

           Description("Set the File Control to use with the NetworkManager."),
           Category("P2P"),
        ]
        public FileControl File
        {
            get { return mFile; }
            set
            {
                if (mFile != null)
                {
                    mFile.SendingFile -= new EventHandler<FileChangedEventArgs>(File_SendingFile);
                }

                mFile = value;
                if (mFile != null)
                {
                    mFile.SetConnectionStatus(mState);
                    mFile.SendingFile += new EventHandler<FileChangedEventArgs>(File_SendingFile);
                }
            }
        }

        #endregion


        /// <summary>
        /// A reference to the custom userName textbox if one is assigned.
        /// </summary>
        private TextBox mUserName = null;
        
        /// <summary>
        /// Gets or sets the username control to use for this networkmanager.
        /// </summary>
        /// <value></value>
        [
           Description("Specify a textbox to use as the network username, otherwise it will use the networkManager's networkname textbox"),
           Category("P2P/controls")
        ]
        public TextBox UserName
        {
            get
            {
                if (mUserName == null)
                    return tbUserName;
                else
                    return mUserName;
            }
            set
            {
                if (value != null)
                {
                    mUserName = value;
                    tbUserName.Visible = false;
                }
            }
        }


        /// <summary>
        /// Relays the event containing the audio file be sent to the lower level P2PLibrary
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">used to find the audio file to send</param>
        private void Audio_SendingStream(object sender, SendStreamEventArgs e)
        {
            if (null != mP2PLib)
            {
                mAudioHttpListener.SendStreamingData(e.FullFileName);

            }
        }

        /// <summary>
        /// Relays the event containing the picture be sent to the lower level P2PLibrary
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">used to find the video file to send</param>
        private void Video_SendingStream(object sender, SendStreamEventArgs e)
        {
            if (null != mP2PLib)
            {
                mVideoHttpListener.SendStreamingData(e.FullFileName);
            }
        }

        /// <summary>
        /// Cancels the audio stream being sent.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void Audio_CancelStream(object sender, EventArgs e)
        {
            if (null != mP2PLib)
            {
                mP2PLib.StreamedAudio.CancelSendingStream();
            }
        }

        /// <summary>
        /// Cancels sending the video stream
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void Video_CancelStream(object sender, EventArgs e)
        {
            if (null != mP2PLib)
            {
                mP2PLib.StreamedVideo.CancelSendingStream();
            }
        }


        /// <summary>
        /// Relays the event containing the picture be sent to the lower level P2PLibrary
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void Picture_SendingPicture(object sender, PictureChangedEventArgs e)
        {
            if (null != mP2PLib)
            {
                mP2PLib.Picture.SendPicture(e.Packet);
            }
        }


        /// <summary>
        /// Relays the event containing the text be sent to the lower level P2PLibrary
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void Chat_SendingText(object sender, ChatChangedEventArgs e)
        {
            if (null != mP2PLib)
            {
                mP2PLib.TextPeerChannelHelper.SendTextMessage(UserName.Text, e.Message);
            }
        }


        /// <summary>
        /// Relays the event containing the file be sent to the lower level P2PLibrary
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void File_SendingFile(object sender, FileChangedEventArgs e)
        {
            if (null != mP2PLib)
            {
                e.Packet.senderNodeName = UserName.Text;
                mP2PLib.FilePeerChannelHelper.SendFile(e.Packet);
            }
        }

        
        /// <summary>
        /// a reference to a custom password if one is assigned. 
        /// </summary>
        private TextBox mPassword = null;
        
        /// <summary>
        /// Gets or Sets the password texbox to use.
        /// </summary>
        /// <value></value>
        [
           Description("Specify a textbox to use as the password textbox, otherwise it will use the networkManager's passwordName textbox"),
           Category("P2P/controls")        
        ]
        public TextBox Password
        {
            get 
            {                
                if (mPassword == null)
                    return tbPassword;
                else
                    return mPassword;            
            }
            set
            {
                mPassword = value;

                if (value != null)
                {

                    tbPassword.Visible = false;
                }
                else
                {
                    tbPassword.Visible = true;
                    this.Show();
                }
            } 
        }


        
        /// <summary>
        /// Shadow the visible property for smart tag..
        /// </summary>
        [
            Description("Visible"),
            Category("Visible"),
            
         ]
        public bool ShadowVisible
        {
            get
            {
                return Visible;
            }
            set
            {
                Visible = value;
            }
        }

        
        /// <summary>
        /// The custom network textbox if assigned. 
        /// </summary>
        private TextBox mNetworkName = null;
        
     
        /// <summary>
        /// The NetworkName TextBox property.  Set this to another textbox if you wish to have this control updated with 
        /// the NetworkName.
        /// </summary>
        [
           Description("Specify a textbox to use as the networkName textbox, otherwise it will use the networkManager's networkName textbox"),
           Category("P2P/controls")           
        ]
        public TextBox NetworkName
        {
            get 
            {                
                if (mNetworkName == null)
                    return tbNetworkName;
                else
                    return mNetworkName;               
            }
            set
            {
                mNetworkName = value;
                if (value != null)
                {
                    tbNetworkName.Visible = false;
                }
                else
                {
                    tbNetworkName.Visible = true;
                    this.Show();
                }
            }
        }


        /// <summary>
        /// A reference to the custom status label
        /// </summary>
        private Label mStatus = null;

        /// <summary>
        /// The Status Label property.  Set this to another textbox if you wish to have this control updated with 
        /// the Status.
        /// </summary>
        [
           Description("Specify a label to use as the networkManagers status, otherwise it will use the networkManager's status label"),
           Category("P2P/controls")
        ]
        public Label Status
        {
            get
            {
                if (mStatus == null)
                    return lblStatus;
                else
                    return mStatus;
            }
            set
            {
                mStatus = value;
                if (value != null)
                {
                    lblStatus.Visible = false;
                }
                else
                {
                    lblStatus.Visible = true;
                    this.Show();
                }
            }
        }


        /// <summary>
        /// A reference to the custom join button network
        /// </summary>        
        private Button mJoinNetwork = null;

        
        /// <summary>
        /// The JoinNetwork button property.  Set this to another button if you wish to have this control use the button
        /// assigned as the Join button.        
        /// </summary>
        [
           Description("Specify a button to use as the join button, otherwise it will use the networkManager's join button"),
           Category("P2P/controls")
        ]
        public Button JoinNetwork
        {
            get 
            {
                if (mJoinNetwork == null)
                    return btnJoin;
                else
                    return mJoinNetwork;
            }
            set
            {
                if (mJoinNetwork != null)
                {
                    mJoinNetwork.Click -= new EventHandler(JoinNetwork_Click);
                }

                mJoinNetwork = value;
                if (value != null)
                {
                    btnJoin.Visible = false;
                    btnJoin.Click -= new EventHandler(JoinNetwork_Click);

                    mJoinNetwork.Click += new EventHandler(JoinNetwork_Click);
                }
                else
                {
                    btnJoin.Visible = true;
                    btnJoin.Click += new EventHandler(JoinNetwork_Click);                    
                }
            }
        }

        /// <summary>
        /// A reference to the custom leave button.
        /// </summary>
        private Button mLeaveNetwork = null;

        /// <summary>
        /// The LeaveNetwork button property.  Set this to another button if you wish to have this control use the button
        /// assigned as the Leave button.
        /// </summary>
        [
           Description("Specify a button to use as the leave button, otherwise it will use the networkManager's leave button"),
           Category("P2P/controls")
        ]
        public Button LeaveNetwork
        {
            get 
            {
                if (mLeaveNetwork == null)
                    return btnLeave;
                else
                    return mLeaveNetwork; 
            }
            set
            {
                if (mLeaveNetwork != null)
                {
                    mLeaveNetwork.Click -= new EventHandler(LeaveNetwork_Click);
                }


                if (value != null)
                {
                    btnLeave.Visible = false;
                    btnLeave.Click -= new EventHandler(LeaveNetwork_Click);

                    mJoinNetwork = value;
                    mJoinNetwork.Click += new EventHandler(JoinNetwork_Click);
                }
                else
                {
                    this.Show();
                    btnLeave.Visible = true;
                    btnLeave.Click += new EventHandler(LeaveNetwork_Click);
                }
            }
        }


     
        /// <summary>
        /// handle leaving the network when someone clicks the leave button
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        void LeaveNetwork_Click(object sender, EventArgs e)
        {
            End();
        }

        /// <summary>
        /// Handle joining the network when someone clicks the join button.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        void JoinNetwork_Click(object sender, EventArgs e)
        {
            Start();
        }


        /// <summary>
        /// the current state of the network.
        /// </summary>
        private SystemState mState = SystemState.LoggedOut;

       
        /// <summary>
        /// This is the main function that takes the input from the textboxes in the window.
        /// It launches all of the threads necessary to stream audio and also starts the peerChannel thread.  This thread
        /// starts the PeerChannelWrapper class and opens a connection to the mesh.
        /// </summary>
        protected void Start()
        {

            //validate input and limit to alphanumeric chars
            Regex regex = new Regex("[a-zA-Z0-9]");
            if (!string.IsNullOrEmpty(UserName.Text) && !string.IsNullOrEmpty(NetworkName.Text) &&
                regex.IsMatch(UserName.Text) && regex.IsMatch(NetworkName.Text))
            {

                UserName.Enabled = false;
                NetworkName.Enabled = false;
                Password.Enabled = false;
                JoinNetwork.Enabled = false;
                LeaveNetwork.Enabled = true;

                //check initial conditions..
                if (!(null == mP2PLib && mPeerChannelThread == null))
                {
                    //error unable to start something didn't get cleaned up correctly when we left the network.
                    MessageBox.Show("An Error occurred when trying to initialize the Network. Please restart the application");
                    return;
                }

                //update the state.
                mState = SystemState.LoggingIn;
                Status.Text = "Status: Connecting...";
                //start up the peerchannel thread which will actually do the work
                mPeerChannelThread = new System.ComponentModel.BackgroundWorker();
                mPeerChannelThread.DoWork += new System.ComponentModel.DoWorkEventHandler(mP2PLibThread);
                mPeerChannelThread.WorkerReportsProgress = true;
                mPeerChannelThread.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(P2PLibThreadCompleted);
                mPeerChannelThread.WorkerSupportsCancellation = true;
                mPeerChannelThread.RunWorkerAsync();

            }
            else
            {
                MessageBox.Show("Please Enter a UserName and NetworkName that contains only alpha-numeric characters");
            }
        }

        
        /// <summary>
        /// if the status changed let all the controls attached know.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">argument that describes how the network changed</param>
        private void P2PLib_StatusChanged(object sender, C4F.VistaP2P.Common.StatusChangedEventArgs e)
        {

            if (Audio != null)
            {
                Audio.SetStatusChanged(UserName.Text, e);
            }

            if (Chat != null)
            {
                Chat.SetStatusChanged(UserName.Text, e);
            }
            if (Picture != null)
            {
                Picture.SetStatusChanged(UserName.Text, e);
            }
            if (File != null)
            {
                File.SetStatusChanged(UserName.Text, e);
            }

            if (Video != null)
            {
                Video.SetStatusChanged(UserName.Text, e);
            }

            if (e.Member == UserName.Text)
            {
                if (e.NewNodeJoined)
                {
                    Utility.SetTextLabel(Status, "Status: Connected...");
                    mState = SystemState.LoggedIn;
                }

                if (e.NodeLeft)
                {
                    Utility.SetTextLabel(Status, "Status: Disconnected...");
                    mState = SystemState.LoggedOut;
                }
            }
            else
            {
                if (e.NewNodeJoined)
                {
                    Chat_ChatChanged(this, new ChatChangedEventArgs(string.Format("{0} just joined!\n", e.Member)));
                }

                if (e.NodeLeft)
                {
                    Chat_ChatChanged(this, new ChatChangedEventArgs(string.Format("{0} just left!\n", e.Member)));
                }
            }                       
        }

        /// <summary>
        /// if we received a chat message notify the chat control if it is attached.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">This class contains the chat message received</param>
        private void Chat_ChatChanged(object sender, ChatChangedEventArgs e)
        {
            if (Chat != null)
            {
              Chat.SetMessageReceived(e);
            }
        }



        /// <summary>
        /// This is the main loop for the peerChannelThread.
        /// It creates a new instance of the PeerChannelWrapper, which opens the mesh.
        /// </summary>
        /// <param name="sender">The p2p library thread</param>
        /// <param name="doWorkArgs">unused</param>
        private void mP2PLibThread(object sender, DoWorkEventArgs doworkargs)
        {

            mP2PLib = new P2PLib();
            if (false == mP2PLib.PeerChannelWrapperStart(UserName.Text, NetworkName.Text, Password.Text))
            {
                //something went wrong opening the connection...bail out 
                End();
            }

            //register for the events from the p2p library that we are interested in
            mP2PLib.StatusChanged += new EventHandler<StatusChangedEventArgs>(P2PLib_StatusChanged);
            mP2PLib.TextPeerChannelHelper.ChatChanged += new EventHandler<ChatChangedEventArgs>(Chat_ChatChanged);
            mP2PLib.Picture.PictureChanged += new EventHandler<PictureChangedEventArgs>(Picture_PictureChanged);
            mP2PLib.FilePeerChannelHelper.FileChanged += new EventHandler<FileChangedEventArgs>(FilePeerChannelHelper_FileChanged);

            
            //If we have an audio control attached, start up the audio http listener so we can bounce the incoming stream off
            //off of a local url and have the media element play it. 
            if (mAudioControl != null)
            {
                mP2PLib.StreamedAudio.StreamChanged += new EventHandler<StreamedChangedEventArgs>(mP2P_StreamedAudioListener);
                ////initialize the audio parameters.
                mAudioHttpListener = new StreamingHttpListener(UserName.Text + "/audio/",
                    AudioBackgroundWorkerThreadFinished, AudioStatusMessage, mP2PLib.StreamedAudio.StartStream,
                    mP2PLib.StreamedAudio.SendStream);
            }

            //If we have a video control attached, start up the audio http listener so we can bounce the incoming stream off
            //off of a local url and have the media element play it. 
            if (mVideoControl != null)
            {
                mP2PLib.StreamedVideo.StreamChanged += new EventHandler<StreamedChangedEventArgs>(mP2P_StreamedVideoListener);

                mVideoHttpListener = new StreamingHttpListener(UserName.Text + "/video/",
                     VideoBackgroundWorkerThreadFinished, VideoStatusMessage, mP2PLib.StreamedVideo.StartStream,
                     mP2PLib.StreamedVideo.SendStream);
            }

            //just sit and wait until the main window tells us to quit.
            //this leaves the peerchannelwrapper instance alive and running in this thread so it can process commands.
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                if (mPeerChannelThread.CancellationPending)
                {
                    //someone told us to quit.
                    mPeerChannelThread = null;
                    break;
                }
            }
        }


        /// <summary>
        /// The status of the video changed, let the video control know.
        /// </summary>
        /// <param name="msg">how the status changed</param>
        public void VideoStatusMessage(string msg)
        {
            if (mVideoControl != null)
            {
                mVideoControl.SetStatusMessage(msg);
            }
        }

        /// <summary>
        /// used to update the status of box of the audio control
        /// </summary>
        /// <param name="msg"></param>
        public void AudioStatusMessage(string msg)
        {
            if (mAudioControl != null)
            {
                mAudioControl.SetStatusMessage(msg);
            }
        }

        
        /// <summary>
        /// A delegate used to force the function call mP2P_StreamedVideoHandler to be made on the
        /// mVideoControl's thread.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">the streamed packet</param>
        delegate void mP2P_StreamedVideoHandler(object sender, StreamedChangedEventArgs e);
        
        /// <summary>
        /// This function gets called when we receive a video message. If it is the first packet in the audio stream
        /// we use a delegate and have the UI(user interface) thread call this function.  When it does CheckAccess will be true
        /// and the controls will be updated.  
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">the streamed packet</param>
        public void mP2P_StreamedVideoListener(object sender, StreamedChangedEventArgs e)
        {            
            if (!mVideoControl.InvokeRequired)
            {
                bool play = false;
                play = e.StreamedPacket.senderNodeName == UserName.Text;


                DialogResult mbr = DialogResult.Cancel;
                if (false == play)
                {
                    mbr = MessageBox.Show(e.StreamedPacket.senderNodeName + " just sent you a Video. Select OK to accept it, otherwise select Cancel", "A file was sent to you!", MessageBoxButtons.OKCancel);
                }

                if (play || mbr == DialogResult.OK)
                {
                    Utility.SetTextLabel(mVideoControl.FirstLineLabel, "Title");
                    Utility.SetTextLabel(mVideoControl.SecondLineLabel, "File");
                    Utility.SetTextLabel(mVideoControl.FirstLineValue, e.StreamedPacket.title);
                    Utility.SetTextLabel(mVideoControl.SecondLineValue, e.StreamedPacket.fileName);

                    mVideoControl.StartPlay("http://localhost:8088/" + UserName.Text + "/video/" + StreamingHttpListener.SafeHttpString(e.StreamedPacket.fileName));
                }
            }
            else
            {
                mVideoHttpListener.StreamedChanged(e);
                if (e.StreamedPacket.packetNumber == 0)
                {
                    mVideoControl.Invoke(new mP2P_StreamedVideoHandler(mP2P_StreamedVideoListener), new object[] {sender, e});
                }
            }
        }


        /// <summary>
        /// This gets called when the audio thread has finished. If the video thread and the peerchannel thread have all quit,
        /// then it sets the state back to initial and allows the user to join another network.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">used to look for an error in the thread</param>
        protected void VideoBackgroundWorkerThreadFinished(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(String.Format("An Error occurred in the Video Processing Thread:{0}", e.Error.Message));
            }
            else
            {
                mVideoHttpListener = null;
                if (mPeerChannelThread == null && mAudioHttpListener == null)
                {
                    _ReInitializeState();
                }
            }
        }


        /// <summary>
        /// A delegate to used to force the function call mP2P_StreamedAudioListener to the audioControl's thread.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        delegate void mP2P_StreamedAudioHandler(object sender, StreamedChangedEventArgs e);
        
        /// <summary>
        /// This function gets called when we receive an audio message. If it is the first packet in the audio stream
        /// we use a delegate and have the UI(user interface) thread call this function.  When it does CheckAccess will be true
        /// and the controls will be updated.  
        /// </summary>
        /// <param name="e">contains the streaming data</param>
        /// <param name="sender">unused.</param>
        public void mP2P_StreamedAudioListener(object sender, StreamedChangedEventArgs e)
        {
            if (!mAudioControl.InvokeRequired)
            {

                
                bool play = false;
                play = e.StreamedPacket.senderNodeName == UserName.Text;

                DialogResult mbr = DialogResult.Cancel;
                if (false == play)
                {
                   mbr = MessageBox.Show(e.StreamedPacket.senderNodeName + " just sent you an audio file . Select OK to accept it, otherwise select Cancel", "A file was sent to you!", MessageBoxButtons.OKCancel);
                }

                if (play || mbr == DialogResult.OK)
                {
                    Utility.SetTextLabel(mAudioControl.FirstLineLabel, "Title");
                    Utility.SetTextLabel(mAudioControl.SecondLineLabel, "Artist");
                    Utility.SetTextLabel(mAudioControl.SecondLineLabel, "Album");

                    Utility.SetTextLabel(mAudioControl.FirstLineValue, e.StreamedPacket.title);
                    Utility.SetTextLabel(mAudioControl.SecondLineValue, e.StreamedPacket.artist);
                    Utility.SetTextLabel(mAudioControl.SecondLineValue, e.StreamedPacket.artist);

                    mAudioControl.StartPlay("http://localhost:8088/" + UserName.Text + "/audio/" + StreamingHttpListener.SafeHttpString(e.StreamedPacket.fileName));
                }
            }
            else
            {
                mAudioHttpListener.StreamedChanged(e);
                if (e.StreamedPacket.packetNumber == 0)
                {
                    mAudioControl.Invoke(new mP2P_StreamedAudioHandler(mP2P_StreamedAudioListener), new object[] { sender, e });
                }
            }
        }


        /// <summary>
        /// This gets called when the audio thread has finished. If the video thread and the peerchannel thread have all quit,
        /// then it sets the state back to initial and allows the user to join another network.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">used to look for an error in the thread</param>
        protected void AudioBackgroundWorkerThreadFinished(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(String.Format("An Error occurred in the Audio Processing Thread:{0}", e.Error.Message));
            }
            else
            {
                mAudioHttpListener = null;
                
                if (mPeerChannelThread == null && mVideoHttpListener == null)
                {
                    _ReInitializeState();
                }
            }
        }


        /// <summary>
        /// The File changed, if there is a file control attached notify it.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">the file packet</param>
        void FilePeerChannelHelper_FileChanged(object sender, FileChangedEventArgs e)
        {
            if (File != null)
            {
                if (e.Packet.senderNodeName != UserName.Text)
                {
                    File.SetFileReceived(e);
                }
            }
        }


        /// <summary>
        /// The Picture changed, if there is a picture control attached notify it.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">the picture data</param>
        void Picture_PictureChanged(object sender, PictureChangedEventArgs e)
        {
            if (Picture != null)
            {
                Picture.SetNewPicture(e);
            }
        }



        /// <summary>
        /// The Thread completed reset back to the intial conditions.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void P2PLibThreadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            
            if (e.Error != null)
            {
                MessageBox.Show(String.Format("An Error occurred in the P2P Thread: {0}", e.Error.Message));
            }
            else
            {
                if (mAudioHttpListener == null && mVideoHttpListener == null)
                {
                    mPeerChannelThread = null;

                    _ReInitializeState();
                }
            }
        }

        /// <summary>
        /// used to ensure that the call to _ReInitializeState is made on the ui controls thread.
        /// </summary>        
        delegate void _ReInitializeHandler();

        /// <summary>
        /// all of the UI back to the default state.
        /// </summary>
        private void _ReInitializeState()
        {
            if (!UserName.InvokeRequired)
            {
                Utility.SetTextLabel(Status,"Status: Disconnected...");
                mState = SystemState.LoggedOut;
                UserName.Enabled = true;
                NetworkName.Enabled = true;
                Password.Enabled = true;
                JoinNetwork.Enabled = true;
                LeaveNetwork.Enabled = false;
            }
            else
            {
                UserName.Invoke(new _ReInitializeHandler(_ReInitializeState), null);
            }
        }

        ///// <summary>
        ///// Logs us out of the network and shuts everything down. 
        ///// </summary>
        protected void End()
        {
            if (null != mP2PLib)
            {
                mP2PLib.Close();
                mP2PLib = null;
            }

            if (null != mVideoHttpListener)
                mVideoHttpListener.Stop();

            if (null != mAudioHttpListener)
                mAudioHttpListener.Stop();

            if (null != mVideoHttpListener)
            {
                mVideoHttpListener.Quit();
            }

            if (null != mAudioHttpListener)
            {
                mAudioHttpListener.Quit();
            }

        
            if (null != mPeerChannelThread)
            {
                mPeerChannelThread.CancelAsync();
            }
        }
    }  
}
